#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/usb/storage.h>
#include <scsi/scsi_proto.h>
#include <asm-generic/errno-base.h>
#include <uapi/asm-generic/errno.h>

/**
 *  USB Mass Storage Class
 *
 * https://www.oreilly.com/library/view/linux-device-drivers/0596005903/ch13.html
 *
 * https://www.kernel.org/doc/html/v4.12/driver-api/usb/writing_usb_driver.html
 *
 * http://www.linux-usb.org/
 *
 * https://embetronicx.com/tutorials/linux/device-drivers/usb-device-driver-basics/
 *
 * https://github.com/Embetronicx/Tutorials/blob/master/Linux/Device_Driver/usb_device_driver/usb_driver.c
 *
 * https://www.usb.org/sites/default/files/Mass_Storage_Specification_Overview_v1.4_2-19-2010.pdf
 *
 * https://www.usb.org/sites/default/files/Mass_Storage_Specification_Overview_v1.4_2-19-2010.pdf
 *
 * https://www.intel.com/content/www/us/en/products/docs/io/universal-serial-bus/universal-serial-bus-specifications.html
 *
 *
 * https://www.linux.it/~rubini/docs/usb/usb.html
 *
 * http://www.linux-usb.org/devices.html
 *
 *
 * https://en.wikipedia.org/wiki/SCSI
 *
 * https://en.wikipedia.org/wiki/SCSI_command
 *
 *
 * https://www.t10.org/lists/2op.htm
 *
 * https://wiki.osdev.org/USB_Mass_Storage_Class_Devices
 *
 *
 * https://support.huawei.com/enterprise/zh/doc/EDOC1000149331/ec501220
 *
 * Devices usually have one or more configurations.
 *
 *
 * Whenever a USB device is attached to the bus it will be enumerated by the
 USB subsystem - i.e an unique device number (1-127) is assigned and then the
 device descriptor is read. Such a desciptor is a data structure which contains
 information about the device and its properties.


 Configurations often have one or more interfaces.

 Interfaces usually have one or more settings.

 Interfaces have zero or more endpoints.
 *
 *
 *  lsusb
 *  usb-devices
 *
 *  Bulk Only Mass Storage Devices are the most common type of USB storage device, and they include most USB thumb drives, hard drives, CD,
 *  DVD and Blu-ray drives as well. They normally use the SCSI Command Set,
 *  but the USB Mass Storage specifications allow devices to use other command sets as well.
 *
 *  USB Mass Storage Class Bulk-Only (BBB) Transport
 *
 *  Universal Serial Bus Specification
 *
 *
 *  In SCSI terminology, communication takes place between an initiator and a target. The initiator sends a command to the target, which then responds.
 *  SCSI commands are sent in a Command Descriptor Block (CDB).
 *  The CDB consists of a one byte operation code followed by five or more bytes containing command-specific parameters.
 *
 *
 *  At the end of the command sequence, the target returns a status code byte, such as 00h for success,
 *   02h for an error (called a Check Condition), or 08h for busy
 *
 *There are four categories of SCSI commands: N (non-data), W (writing data from initiator to target), R (reading data), and B (bidirectional).
 *
 *Test unit ready: Queries device to see if it is ready for data transfers (disk spun up, media loaded, etc.).
 Inquiry: Returns basic device information.
 Request sense: Returns any error codes from the previous command that returned an error status.
 Send diagnostic and Receive diagnostic results: runs a simple self-test, or a specialised test defined in a diagnostic page.
 Start/Stop unit: Spins disks up and down, or loads/unloads media (CD, tape, etc.).
 Read capacity: Returns storage capacity.
 Format unit: Prepares a storage medium for use. In a disk, a low level format will occur. Some tape drives will erase the tape in response to this command.
 Read: (four variants): Reads data from a device.
 Write: (four variants): Writes data to a device.
 Log sense: Returns current information from log pages.
 Mode sense: Returns current device parameters from mode pages.
 Mode select: Sets device parameters in a mode page.


 SCSI commands are sent in a command descriptor block (CDB), which consists of a one byte operation code (opcode) followed by five
 or more bytes containing command-specific parameters.
 Upon receiving and processing the CDB the device will return a status code byte and other information.

 The target usually provides the initiators one or more logical unit numbers (LUN).

 In a storage area, a LUN often represents a SCSI disk on which a host can perform a read and write operation.

 Every endpoint in a device can handle a queue of urbs, so that multiple urbs can be sent to the same endpoint before the queue is empty


 T:  Bus=06 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=5000 MxCh= 0
 D:  Ver= 3.20 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs=  1
 P:  Vendor=0951 ProdID=1666 Rev=01.10
 S:  Manufacturer=Kingston
 S:  Product=DataTraveler 3.0
 S:  SerialNumber=E0D55EA573EB165178570CE3
 C:  #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=144mA
 I:  If#=0x0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=andy_usbdriver

 *
 */

#define USB_VENDOR_ID  (0x0951)
#define USB_PRODUCT_ID (0x1666)

//#define TestUnitReady

#define Inquiry
#define  MYTAG  1333

//#define  inquiry_device_identification
#define  inquiry_supported_vpd_pages



#define PRINT_USB_ENDPOINT_DESCRIPTOR( i )                         \
{                                                                   \
    pr_info("USB_INTERFACE_DESCRIPTOR:\n");                         \
    pr_info("-----------------------------\n");                     \
    pr_info("bLength: 0x%x\n", i.bLength);                          \
    pr_info("bDescriptorType: 0x%x\n", i.bDescriptorType);          \
    pr_info("bInterfaceNumber: 0x%x\n", i.bInterfaceNumber);        \
    pr_info("bAlternateSetting: 0x%x\n", i.bAlternateSetting);      \
    pr_info("bNumEndpoints: 0x%x\n", i.bNumEndpoints);              \
    pr_info("bInterfaceClass: 0x%x\n", i.bInterfaceClass);          \
    pr_info("bInterfaceSubClass: 0x%x\n", i.bInterfaceSubClass);    \
    pr_info("bInterfaceProtocol: 0x%x\n", i.bInterfaceProtocol);    \
    pr_info("iInterface: 0x%x\n", i.iInterface);                    \
    pr_info("\n");                                                  \
}

struct usb_device *usb_dev;

int endpoint_out_addr;
int endpoint_in_addr;

struct bulk_cs_wrap *bcs;
struct bulk_cb_wrap *bcb;

void complete_callback(struct urb *urb) {
	dev_info(&urb->dev->dev, "complete_callback\n");
	dev_info(&urb->dev->dev, "status %d\n", urb->status);
	dev_info(&urb->dev->dev, "actual_length %d\n", urb->actual_length);
	usb_free_urb(urb);
}

void complete_callback2(struct urb *urb) {
	dev_info(&urb->dev->dev, "complete_callback2\n");
	dev_info(&urb->dev->dev, "status %d\n", urb->status);
	dev_info(&urb->dev->dev, "actual_length %d\n", urb->actual_length);
	dev_info(&urb->dev->dev, "bcs->Status %d\n", bcs->Status);

//	https://wiki.osdev.org/USB_Mass_Storage_Class_Devices
	switch (bcs->Status) {
	case 0:
		dev_info(&urb->dev->dev, "bcs->Status success");
		break;
	case 1:
		dev_info(&urb->dev->dev, "bcs->Status failed");
		break;
	default:
		break;
	}

	usb_free_urb(urb);
}

u8 *buffer;

#define MAX_BYTES 1024

typedef struct {
	u8 a1;
	u8 a2;
	u8 a3;
	u8 a4;
	u8 a5;
	u8 a6;
	u8 a7;
	u8 a8;
	u8 a9;
	u8 a10;
	u8 a11;
	u8 a12;
	u8 Status;
	u8 a14;
	u8 a15;
	u8 a16;
} struct1;

/*
 Define the SCSI INQUIRY Unit Serial Number structure.
 */

typedef struct SCSI_Inquiry_UnitSerialNumber {
	u8 PeripheralDeviceType :5; /* Byte 0 Bits 0-4 */
	u8 PeripheralQualifier :3; /* Byte 0 Bits 5-7 */
	u8 PageCode; /* Byte 1 */
	u8 :8; /* Byte 2 */
	u8 PageLength; /* Byte 3 */
	u8 ProductSerialNumber[12]; /* Bytes 4-16 */
} SCSI_Inquiry_UnitSerialNumber;

/*
 Define the SCSI INQUIRY Unit Serial Number structure.
 */

typedef struct SCSI_Inquiry_device_identification {
	u8 PeripheralDeviceType :5; /* Byte 0 Bits 0-4 */
	u8 PeripheralQualifier :3; /* Byte 0 Bits 5-7 */
	u8 PageCode; /* Byte 1 */
	u8 :8; /* Byte 2 */
	u8 PageLength; /* Byte 3 */
	u8 device_identification[28]; /* Bytes 4-16 */
} SCSI_Inquiry_device_identification;

typedef struct SCSI_Inquiry_supported_pages {
	u8 PeripheralDeviceType :5; /* Byte 0 Bits 0-4 */
	u8 PeripheralQualifier :3; /* Byte 0 Bits 5-7 */
	u8 PageCode; /* Byte 1 */
	u8 :8; /* Byte 2 */
	u8 PageLength; /* Byte 3 */
	u8 pages[3]; /* Bytes 4-16 */
} SCSI_Inquiry_supported_pages;

void complete_callback3(struct urb *urb) {
	struct1 *buf = (struct1*) buffer;
	dev_info(&urb->dev->dev, "complete_callback3\n");
	dev_info(&urb->dev->dev, "buffer = %s\n", buffer);
	dev_info(&urb->dev->dev, "status %d\n", urb->status);
	dev_info(&urb->dev->dev, "actual_length %d\n", urb->actual_length);
	dev_info(&urb->dev->dev, "buf.a1 = %x\n", buf->a1);
	dev_info(&urb->dev->dev, "buf.a2 = %x\n", buf->a2);
	dev_info(&urb->dev->dev, "buf.a3 = %x\n", buf->a3);
	dev_info(&urb->dev->dev, "buf.a4 = %x\n", buf->a4);
	dev_info(&urb->dev->dev, "buf.a5 = %x\n", buf->a5);
	dev_info(&urb->dev->dev, "buf.a6 = %x\n", buf->a6);
	dev_info(&urb->dev->dev, "buf.a7 = %x\n", buf->a7);
	dev_info(&urb->dev->dev, "buf.a8 = %x\n", buf->a8);
	dev_info(&urb->dev->dev, "buf.a9 = %x\n", buf->a9);
	dev_info(&urb->dev->dev, "buf.a10 = %x\n", buf->a10);
	dev_info(&urb->dev->dev, "buf.a11 = %x\n", buf->a11);
	dev_info(&urb->dev->dev, "buf.a12 = %x\n", buf->a12);
	dev_info(&urb->dev->dev, "buf.Status = %x\n", buf->Status);
	dev_info(&urb->dev->dev, "buf.a14 = %x\n", buf->a14);
	dev_info(&urb->dev->dev, "buf.a15 = %x\n", buf->a15);
	dev_info(&urb->dev->dev, "buf.a16 = %x\n", buf->a16);
	usb_free_urb(urb);
}

#ifdef PAGE_CODE_80H
static void inquiry_serial_number(void) {
	int status;
	int actual_length2=0;
	SCSI_Inquiry_UnitSerialNumber  *usb_serial_number;

	bcb = kmalloc(sizeof(*bcb), GFP_KERNEL);
	memset(bcb, 0, sizeof(struct bulk_cb_wrap));

	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
	bcb->DataTransferLength = 1024;
	bcb->Flags = US_BULK_FLAG_IN;
	bcb->Lun = 0;
	bcb->Length = 6;
	bcb ->Tag = 0x13;

	/**
	 * inquiry
	 */
	bcb->CDB[0] = 0x12;

	/**
	 *
	 */
	bcb->CDB[1] = 0x01;

//	80h = Unit serial number page
	bcb->CDB[2] = 0x80;

//	bcb->CDB[1] = 0x00;
//	bcb->CDB[2] = 0x00;
	bcb->CDB[3] = maxBytes & 0xff;
	bcb->CDB[4] = maxBytes & 0xff00 >> 8;
	bcb->CDB[5] = 0x00;

	buffer = kmalloc(maxBytes, GFP_KERNEL);
	memset(buffer, 0, maxBytes);

	inquery = usb_alloc_urb(0, GFP_KERNEL);
	response = usb_alloc_urb(0, GFP_KERNEL);
	response2 = usb_alloc_urb(0, GFP_KERNEL);
//	 The CDB consists of a one byte operation code followed by five or more bytes containing command-specific parameters.

	status = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, endpoint_out_addr),
			bcb, US_BULK_CB_WRAP_LEN, &actual_length2,50);
		if(status!=0){
			dev_info(&usb_dev->dev, "usb_bulk_msg fail \n");
			return ;
		}

		dev_info(&usb_dev->dev, "usb_bulk_msg success \n");
						dev_info(&usb_dev->dev, "actual_length2 %d\n",actual_length2);


	//read response from usb device;
	status = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, endpoint_in_addr),
			buffer, MAX_BYTES, &actual_length2,50);
	if(status!=0){
		dev_info(&usb_dev->dev, "usb_bulk_msg fail \n");
		return ;
	}
					dev_info(&usb_dev->dev, "usb_bulk_msg success \n");
					dev_info(&usb_dev->dev, "actual_length2 %d\n",actual_length2);

//					SerialNumber= E0D5 5EA5 73EB 1651 7857 0CE3

					if(actual_length2!=13){
						usb_serial_number = (SCSI_Inquiry_UnitSerialNumber *)buffer;
						dev_info(&usb_dev->dev, "PeripheralDeviceType = 0x%x\n",usb_serial_number->PeripheralDeviceType);
						dev_info(&usb_dev->dev, "PageCode = 0x%x\n",usb_serial_number->PageCode);
						dev_info(&usb_dev->dev, "PageLength = %d\n",usb_serial_number->PageLength);

						dev_info(&usb_dev->dev, "ProductSerialNumber = %s\n",usb_serial_number->ProductSerialNumber);

						dev_info(&usb_dev->dev, "ProductSerialNumber 0= %c\n",usb_serial_number->ProductSerialNumber[0]);
						dev_info(&usb_dev->dev, "ProductSerialNumber 1= %c\n",usb_serial_number->ProductSerialNumber[1]);
						dev_info(&usb_dev->dev, "ProductSerialNumber 2= %c\n",usb_serial_number->ProductSerialNumber[2]);
						dev_info(&usb_dev->dev, "ProductSerialNumber 3= %c\n",usb_serial_number->ProductSerialNumber[3]);

						dev_info(&usb_dev->dev, "ProductSerialNumber 4= %c\n",usb_serial_number->ProductSerialNumber[4]);
						dev_info(&usb_dev->dev, "ProductSerialNumber 5= %c\n",usb_serial_number->ProductSerialNumber[5]);
						dev_info(&usb_dev->dev, "ProductSerialNumber 6= %c\n",usb_serial_number->ProductSerialNumber[6]);
						dev_info(&usb_dev->dev, "ProductSerialNumber 7= %c\n",usb_serial_number->ProductSerialNumber[7]);

						dev_info(&usb_dev->dev, "ProductSerialNumber 8= %c\n",usb_serial_number->ProductSerialNumber[8]);
						dev_info(&usb_dev->dev, "ProductSerialNumber 9= %c\n",usb_serial_number->ProductSerialNumber[9]);
						dev_info(&usb_dev->dev, "ProductSerialNumber 10= %c\n",usb_serial_number->ProductSerialNumber[10]);
						dev_info(&usb_dev->dev, "ProductSerialNumber 11= %c\n",usb_serial_number->ProductSerialNumber[11]);


						  //now read bcs
								bcs = kmalloc(sizeof(*bcs), GFP_ATOMIC);
								memset(bcs, 0, sizeof(struct bulk_cb_wrap));
									bcs->Status=10; //just check to see if the result status will be changed to 0 by the usb device  .
								status = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, endpoint_in_addr),
											bcs, US_BULK_CS_WRAP_LEN, &actual_length2,
									                 50);
									if(status==0){
										dev_info(&usb_dev->dev, "actual_length2 %d\n",actual_length2);
										dev_info(&usb_dev->dev, "bcs Status %d\n",bcs->Status);
										if(bcs->Status==0){
											dev_info(&usb_dev->dev, "bcs Status success\n");
										}
										dev_info(&usb_dev->dev, "bcs Tag 0x%x\n",bcs->Tag);
									}

					}

}
#endif


#ifdef  inquiry_supported_vpd_pages
static void inquiry_supported_vpd_pages_00h(void) {
	int status;
	int actual_length = 0;
	int i;
//	SCSI_Inquiry_device_identification *device_identification;

	SCSI_Inquiry_supported_pages *supported_pages;

	bcb = kmalloc(sizeof(*bcb), GFP_KERNEL);
	memset(bcb, 0, sizeof(struct bulk_cb_wrap));

	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
	bcb->DataTransferLength = MAX_BYTES;
	bcb->Flags = US_BULK_FLAG_IN;
	bcb->Lun = 0;
	bcb->Length = 6;
	bcb->Tag = MYTAG;

	/**
	 * inquiry
	 */
	bcb->CDB[0] = 0x12;

	/**
	 *
	 */
	bcb->CDB[1] = 0x01;

//	80h = Unit serial number page
//	00h = Supported vital product pages
//	83h Device Identification
	bcb->CDB[2] = 0x00;
	bcb->CDB[3] = MAX_BYTES & 0xff;
	bcb->CDB[4] = MAX_BYTES & 0xff00 >> 8;
	bcb->CDB[5] = 0x00;

	buffer = kmalloc(MAX_BYTES, GFP_KERNEL);
	memset(buffer, 0, MAX_BYTES);

//	 The CDB consists of a one byte operation code followed by five or more bytes containing command-specific parameters.

	status = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, endpoint_out_addr),
			bcb, US_BULK_CB_WRAP_LEN, &actual_length, 1000);
	if (status) {
		dev_info(&usb_dev->dev, "usb_bulk_msg fail , status: %d\n",status);
		return;
	}

	dev_info(&usb_dev->dev, "usb_bulk_msg success \n");
	dev_info(&usb_dev->dev, "actual_length = %d\n", actual_length);
	if(actual_length==US_BULK_CB_WRAP_LEN){
		dev_info(&usb_dev->dev, "actual_length success");
	}

	dev_info(&usb_dev->dev, "start receive data from usb device\n");
	//read response from usb device;
	status = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, endpoint_in_addr),
			buffer, MAX_BYTES, &actual_length, 1000);
	if (status) {
		dev_info(&usb_dev->dev, "usb_bulk_msg fail , status: %d\n",status);
		return;
	}
	dev_info(&usb_dev->dev, "usb_bulk_msg success \n");
	dev_info(&usb_dev->dev, "actual_length = %d\n", actual_length);

	if (actual_length == 0){
		dev_info(&usb_dev->dev, "no date received\n");
		return ;
	}

	if (actual_length != 13) {
		supported_pages = (SCSI_Inquiry_supported_pages*) buffer;
		dev_info(&usb_dev->dev, "PeripheralDeviceType = 0x%x\n",
				supported_pages->PeripheralDeviceType);
		dev_info(&usb_dev->dev, "PageCode = 0x%x\n",
				supported_pages->PageCode);
		dev_info(&usb_dev->dev, "PageLength = %d\n",
				supported_pages->PageLength);

		// 0x00 0x80 0x83
		dev_info(&usb_dev->dev, "supported_pages = %s\n",supported_pages->pages);

		for (i = 0; i < supported_pages->PageLength; i++) {
			dev_info(&usb_dev->dev, "device_identification.%d = 0x%x\n",i+1,
					*(supported_pages->pages + i));
//			dev_info(&usb_dev->dev, "device_identification.%d = %c\n",i+1,
//							*(supported_pages->pages + i));
		}

		//now read bcs
		bcs = kmalloc(sizeof(*bcs), GFP_ATOMIC);
		memset(bcs, 0, sizeof(struct bulk_cb_wrap));
		bcs->Status = 10; //just check to see if the result status will be changed to 0 by the usb device  .
		status = usb_bulk_msg(usb_dev,
				usb_rcvbulkpipe(usb_dev, endpoint_in_addr), bcs,
				US_BULK_CS_WRAP_LEN, &actual_length, 50);
		if (status == 0) {
			dev_info(&usb_dev->dev, "actual_length2 %d\n", actual_length);
			dev_info(&usb_dev->dev, "bcs Status %d\n", bcs->Status);
			if (bcs->Status == 0) {
				dev_info(&usb_dev->dev, "bcs Status success\n");
			}
			dev_info(&usb_dev->dev, "bcs Tag 0x%x\n", bcs->Tag);
			if (bcs->Tag == MYTAG) {
				dev_info(&usb_dev->dev, "bcs Tag success\n");
			}
			else{
				dev_info(&usb_dev->dev, "bcs Tag fail\n");
			}
		}
	}

}
#endif


#ifdef  inquiry_device_identification
static void inquiry_device_identification(void) {
	int status;
	int actual_length = 0;
	int i;
	SCSI_Inquiry_device_identification *device_identification;

	bcb = kmalloc(sizeof(*bcb), GFP_KERNEL);
	memset(bcb, 0, sizeof(struct bulk_cb_wrap));

	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
	bcb->DataTransferLength = MAX_BYTES;
	bcb->Flags = US_BULK_FLAG_IN;
	bcb->Lun = 0;
	bcb->Length = 6;
	bcb->Tag = 0x13;

	/**
	 * inquiry
	 */
	bcb->CDB[0] = 0x12;

	/**
	 *
	 */
	bcb->CDB[1] = 0x01;

//	80h = Unit serial number page
//	00h = Supported vital product pages
//	83h Device Identification
	bcb->CDB[2] = 0x00;
	bcb->CDB[3] = MAX_BYTES & 0xff;
	bcb->CDB[4] = MAX_BYTES & 0xff00 >> 8;
	bcb->CDB[5] = 0x00;

	buffer = kmalloc(MAX_BYTES, GFP_KERNEL);
	memset(buffer, 0, MAX_BYTES);

//	 The CDB consists of a one byte operation code followed by five or more bytes containing command-specific parameters.

	status = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, endpoint_out_addr),
			bcb, US_BULK_CB_WRAP_LEN, &actual_length, 1000);
	if (status) {
		dev_info(&usb_dev->dev, "usb_bulk_msg failed \n");
		return;
	}

	dev_info(&usb_dev->dev, "usb_bulk_msg success \n");
	dev_info(&usb_dev->dev, "actual_length = %d\n", actual_length);
	if(actual_length==US_BULK_CB_WRAP_LEN){
		dev_info(&usb_dev->dev, "actual_length success");
	}

	//read response from usb device;
	status = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, endpoint_in_addr),
			buffer, MAX_BYTES, &actual_length, 1000);
	if (status) {
		dev_info(&usb_dev->dev, "usb_bulk_msg fail \n");
		return;
	}
	dev_info(&usb_dev->dev, "usb_bulk_msg success \n");
	dev_info(&usb_dev->dev, "actual_length = %d\n", actual_length);

//					SerialNumber= E0D5 5EA5 73EB 1651 7857 0CE3

	if (actual_length != 13) {
		device_identification = (SCSI_Inquiry_device_identification*) buffer;
		dev_info(&usb_dev->dev, "PeripheralDeviceType = 0x%x\n",
				device_identification->PeripheralDeviceType);
		dev_info(&usb_dev->dev, "PageCode = 0x%x\n",
				device_identification->PageCode);
		dev_info(&usb_dev->dev, "PageLength = %d\n",
				device_identification->PageLength);

//						dev_info(&usb_dev->dev, "device_identification = %c\n",*(device_identification->device_identification));

		for (i = 0; i < device_identification->PageLength; i++) {
			dev_info(&usb_dev->dev, "device_identification = %c\n",
					*(device_identification->device_identification + i));
		}

		//now read bcs
		bcs = kmalloc(sizeof(*bcs), GFP_ATOMIC);
		memset(bcs, 0, sizeof(struct bulk_cb_wrap));
		bcs->Status = 10; //just check to see if the result status will be changed to 0 by the usb device  .
		status = usb_bulk_msg(usb_dev,
				usb_rcvbulkpipe(usb_dev, endpoint_in_addr), bcs,
				US_BULK_CS_WRAP_LEN, &actual_length, 50);
		if (status == 0) {
			dev_info(&usb_dev->dev, "actual_length2 %d\n", actual_length);
			dev_info(&usb_dev->dev, "bcs Status %d\n", bcs->Status);
			if (bcs->Status == 0) {
				dev_info(&usb_dev->dev, "bcs Status success\n");
			}
			dev_info(&usb_dev->dev, "bcs Tag 0x%x\n", bcs->Tag);
			if (bcs->Tag == 0x13) {
				dev_info(&usb_dev->dev, "bcs Tag success\n");
			}
		}

	}

}
#endif

#ifdef TestUnitReady
static void test_unit_ready(void) {
	int status;
	bcb = kmalloc(sizeof(*bcb), GFP_KERNEL);
	memset(bcb, 0, sizeof(struct bulk_cb_wrap));

	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
	bcb->DataTransferLength = 0;
	bcb->Flags = 0;
	bcb->Lun = 0;
	bcb->Lun = 0;
	bcb->Length = 6;

	bcb->CDB[0] = 0x00;
	bcb->CDB[1] = 0x00;
	bcb->CDB[2] = 0x00;
	bcb->CDB[3] = 0x00;
	bcb->CDB[4] = 0x00;
	bcb->CDB[5] = 0x00;

	bcs = kmalloc(sizeof(*bcs), GFP_KERNEL);
		memset(bcs, 0, sizeof(struct bulk_cb_wrap));
		buffer = kmalloc(96, GFP_KERNEL);
	inquery = usb_alloc_urb(0, GFP_KERNEL);
	response =  usb_alloc_urb(0, GFP_KERNEL);
	response2 =  usb_alloc_urb(0, GFP_KERNEL);
//	 The CDB consists of a one byte operation code followed by five or more bytes containing command-specific parameters.

	usb_fill_bulk_urb(inquery, usb_dev,
			usb_sndbulkpipe(usb_dev, endpoint_out_addr), bcb, US_BULK_CB_WRAP_LEN,
			complete_callback, NULL);

	status = usb_submit_urb(inquery, GFP_ATOMIC);
	if (status < 0) {
		pr_info("usb_submit_urb send fail\n");
	} else {
		pr_info("usb_submit_urb send success\n");
	}

	bcs->Status=10; //just check to see if the result status will be changed to 0 by the usb device  .
	 usb_fill_bulk_urb(response, usb_dev,
			 usb_rcvbulkpipe(usb_dev, endpoint_in_addr), bcs, US_BULK_CS_WRAP_LEN,
			complete_callback2, NULL);

	 status = usb_submit_urb(response, GFP_ATOMIC);

	if (status < 0) {
			pr_info("usb_submit_urb read fail\n");
		} else {
			pr_info("usb_submit_urb read success\n");
		}

	 usb_fill_bulk_urb(response2, usb_dev,
				 usb_rcvbulkpipe(usb_dev, endpoint_in_addr), buffer, 96,
				complete_callback3, NULL);

		 status = usb_submit_urb(response2, GFP_ATOMIC);

		if (status < 0) {
				pr_info("usb_submit_urb read2 fail\n");
			} else {
				pr_info("usb_submit_urb read2 success\n");
			}

}
#endif

struct vdsk_dev {
	unsigned long size;
	u8 *data;
	spinlock_t lock;
	struct gendisk *mydisk;
	struct request_queue *queue;
};

struct vdsk_dev *dev;

int myprobe(struct usb_interface *interface, const struct usb_device_id *id) {
	unsigned int i;
	unsigned int endpoints_count;
	int endpoint_type;

	int interfaceClass;
	int interfaceSubClass;
	int interfaceProtocol;

	struct usb_endpoint_descriptor endpoint_descriptor;
	struct usb_host_interface *iface_desc = interface->cur_altsetting;

	usb_dev = interface_to_usbdev(interface);

	dev_info(&interface->dev, "myprobe start\n");
	dev_info(&interface->dev, "idVendor = %x\n", id->idVendor);
	dev_info(&interface->dev, "idProduct = %x\n", id->idProduct);

	endpoints_count = iface_desc->desc.bNumEndpoints;
	dev_info(&interface->dev, "endpoints_count = %d\n", endpoints_count);

	interfaceClass = iface_desc->desc.bInterfaceClass;

	interfaceSubClass = iface_desc->desc.bInterfaceSubClass;

	interfaceProtocol = iface_desc->desc.bInterfaceProtocol;

//	dev_info(&interface->dev, "bInterfaceClass = 0x%x\n", interfaceClass);

//	dev_info(&interface->dev, "interfaceSubClass = 0x%x\n", interfaceSubClass);

//	dev_info(&interface->dev, "interfaceProtocol = 0x%x\n", interfaceProtocol);

	switch (interfaceClass) {
	case USB_CLASS_MASS_STORAGE:
		dev_info(&interface->dev, "interfaceClass: USB_CLASS_MASS_STORAGE\n");
		break;
	default:
		dev_info(&interface->dev, "never \n");
		break;
	}

	switch (interfaceSubClass) {
	case USB_SC_SCSI:
		dev_info(&interface->dev, "interfaceSubClass: USB_SC_SCSI\n");
		break;
	default:
		dev_info(&interface->dev, "never \n");
		break;
	}

	switch (interfaceProtocol) {
	case USB_PR_BULK:
		dev_info(&interface->dev, "interfaceProtocol: USB_PR_BULK\n");
		break;
	default:
		dev_info(&interface->dev, "never \n");
		break;
	}

	for (i = 0; i < endpoints_count; i++) {
		endpoint_descriptor = iface_desc->endpoint[i].desc;

		if (usb_endpoint_is_bulk_out(&endpoint_descriptor)) {
			endpoint_out_addr = endpoint_descriptor.bEndpointAddress;
		}

		if (usb_endpoint_is_bulk_in(&endpoint_descriptor)) {
			endpoint_in_addr = endpoint_descriptor.bEndpointAddress;
		}

		dev_info(&interface->dev, "endpoint %d bDescriptorType = %d\n", i,
				endpoint_descriptor.bDescriptorType);

		dev_info(&interface->dev, "endpoint %d bEndpointAddress = %d\n", i,
				endpoint_descriptor.bEndpointAddress);

		endpoint_type = endpoint_descriptor.bmAttributes
				& USB_ENDPOINT_XFERTYPE_MASK;

		switch (endpoint_type) {
		case USB_ENDPOINT_XFER_CONTROL:
			dev_info(&interface->dev, "control endpoint\n");
			break;
		case USB_ENDPOINT_XFER_ISOC:
			dev_info(&interface->dev, "ISOCHRONOUS endpoint\n");
			break;
		case USB_ENDPOINT_XFER_BULK:
			dev_info(&interface->dev, "bulk endpoint\n");
			break;
		case USB_ENDPOINT_XFER_INT:
			dev_info(&interface->dev, "Interrupt  endpoint\n");
			break;
		default:
			dev_info(&interface->dev, "never \n");
			break;
		}
	}
//	test_unit_ready();
#ifdef  inquiry_supported_vpd_pages
	inquiry_supported_vpd_pages_00h();
#endif
//	inquiry_device_identification();

//	my_timer.function = my_timer_func;
//			my_timer.expires= jiffies + msecs_to_jiffies(1000);
//			my_timer.data=0;
//			init_timer(&my_timer);
//			mod_timer(&my_timer, jiffies + 2 * HZ);

	dev_info(&interface->dev, "myprobe end\n");
	return 0;
}

void mydisconnect(struct usb_interface *intf) {
	dev_info(&intf->dev, "mydisconnect\n");
	kfree(bcs);
	kfree(bcb);
	kfree(buffer);
}

static struct usb_device_id my_id_table[] = { { USB_INTERFACE_INFO(
USB_CLASS_MASS_STORAGE,
USB_SC_SCSI, USB_PR_BULK) }, { } };

//static struct usb_device_id my_id_table[] = { { USB_DEVICE(USB_VENDOR_ID,
//USB_PRODUCT_ID) }, { } };

MODULE_DEVICE_TABLE(usb, my_id_table);

static struct usb_driver my_usbdriver = { .name = "andy_usbdriver", .probe =
		myprobe, .disconnect = mydisconnect, .id_table = my_id_table, };

module_usb_driver( my_usbdriver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andy");
